﻿namespace Pyramid
{
    using System;

    /// <summary>
    /// Represents a 3-trit balanced ternary integer; also called a Triad.
    /// </summary>
    public partial struct Trybble
    {
        /// <summary>
        /// The maximum numeric value that a Trybble can store.
        /// </summary>
        private const sbyte MAX_VALUE = 13; //(3 ^ 3 - 1) / 2

        /// <summary>
        /// The minimum numeric value that a Trybble can store.
        /// </summary>
        private const sbyte MIN_VALUE = -MAX_VALUE;

        /// <summary>
        /// Represents the largest possible value of a
        /// <see cref="Setun.Trybble"/>. This field is read-only.
        /// </summary>
        public static readonly Trybble MaxValue = new Trybble(MAX_VALUE);

        /// <summary>
        /// Represents the smallest possible value of a
        /// <see cref="Setun.Trybble"/>. This field is read-only.
        /// </summary>
        public static readonly Trybble MinValue = new Trybble(MIN_VALUE);

        /// <summary>
        /// The number of balanced ternary digits per Trybble. This field is constant.
        /// </summary>
        public const int TritsPerTrybble = 3;

        private readonly sbyte value;

        private Trybble(sbyte value)
        {
            Util.AssertRange(value, MIN_VALUE, MAX_VALUE);

            this.value = value;
        }

        public static bool TryParse(string s, out Trybble result)
        {
            s = s.ToUpperInvariant();

            // TODO This currently throws exceptions on Ternary or Nonary format
            // errors; being a TryXXX method, it should return false instead.

            if (s.StartsWith(Util.TERNARY_FORMAT_PREFIX))
            {
                return TryParseTernary(s, out result);
            }
            else if (s.StartsWith(Util.NONARY_FORMAT_PREFIX))
            {
                return TryParseNonary(s, out result);
            }
            else
            {
                return TryParseShort(s, out result);
            }
        }

        private static bool TryParseShort(string s, out Trybble result)
        {
            sbyte parsedValue;

            if (sbyte.TryParse(s, out parsedValue) && parsedValue <= MAX_VALUE && parsedValue >= MIN_VALUE)
            {
                result = new Trybble(parsedValue);
                return true;
            }
            else
            {
                result = 0;
                return false;
            }
        }

        private static bool TryParseTernary(string s, out Trybble result)
        {
            long parsedValue = Util.ParseTernaryString(s);

            if (parsedValue <= MAX_VALUE && parsedValue >= MIN_VALUE)
            {
                result = new Trybble((sbyte)parsedValue);
                return true;
            }
            else
            {
                result = 0;
                return false;
            }
        }

        private static bool TryParseNonary(string s, out Trybble result)
        {
            long parsedValue = Util.ParseNonaryString(s);

            if (parsedValue <= MAX_VALUE && parsedValue >= MIN_VALUE)
            {
                result = new Trybble((sbyte)parsedValue);
                return true;
            }
            else
            {
                result = 0;
                return false;
            }
        }

        public static Trybble Parse(string s)
        {
            Trybble result;

            if (TryParse(s, out result))
            {
                return result;
            }
            else
            {
                throw new FormatException("Input string was not in a correct format.");
            }
        }

        #region Implicit/Explicit Operators

        [CLSCompliant(false)]
        public static implicit operator Trybble(sbyte value)
        {
            return new Trybble(value);
        }

        [CLSCompliant(false)]
        public static explicit operator sbyte(Trybble value)
        {
            return (sbyte)value.value;
        }

        public static explicit operator Trybble(short value)
        {
            return new Trybble((sbyte)Util.Trim(value, MAX_VALUE, MIN_VALUE));
        }

        public static implicit operator short(Trybble value)
        {
            return value.value;
        }

        public static explicit operator Trybble(int value)
        {
            return new Trybble((sbyte)Util.Trim(value, MAX_VALUE, MIN_VALUE));
        }

        public static implicit operator int(Trybble value)
        {
            return value.value;
        }

        public static explicit operator Trybble(long value)
        {
            return new Trybble((sbyte)Util.Trim(value, MAX_VALUE, MIN_VALUE));
        }

        public static implicit operator long(Trybble value)
        {
            return value.value;
        }

        public static explicit operator Trybble(Tryte value)
        {
            return new Trybble((sbyte)Util.Trim(value, MAX_VALUE, MIN_VALUE));
        }

        public static implicit operator Tryte(Trybble value)
        {
            return value.value;
        }

        public static explicit operator Trybble(Int27 value)
        {
            return new Trybble((sbyte)Util.Trim(value, MAX_VALUE, MIN_VALUE));
        }

        public static implicit operator Int27(Trybble value)
        {
            return value.value;
        }

        #endregion

        public override string ToString()
        {
            return value.ToString();
        }
    }

    public partial struct Trybble
        : ITernary
    {
        public int TritSize
        {
            get
            {
                return TritsPerTrybble;
            }
        }

        public Trivalent[] ToTritArray()
        {
            return Util.ToTritArray(this.value, TritsPerTrybble);
        }

        public Tryte ToTryte()
        {
            return (Tryte)this.value;
        }

        public Int27 ToInt27()
        {
            return (Int27)this.value;
        }
    }

    public partial struct Trybble
        : IComparable
    {
        #region IComparable Members

        public int CompareTo(object obj)
        {
            return Util.CompareTo(this, obj);
        }

        #endregion
    }

    public partial struct Trybble
        : IComparable<Trybble>
    {
        #region IComparable<Trybble> Members

        public int CompareTo(Trybble other)
        {
            return this.value.CompareTo(other.value);
        }

        #endregion
    }
}
